﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;


namespace GaleSong
{
    public static class AssemblySource
    {
        /// <summary>
        /// The singleton instance of the AssemblySource used by the framework.
        /// </summary>
        public static readonly IObservableCollection<Assembly> Instance = new BindableCollection<Assembly>();
    }

    public static class ViewHelper
    {
        public static Func<string, string> ResolveWpfName =
            name =>
            {
                name = name.Replace("GaleSong", "GaleSong.UI");
                return name.EndsWith("Dialog") ? name.Replace("Dialog", "Window") : name + "Control";
            };

        public static Func<string, string> ResolveWinformName =
            name =>
            {
                name = name.Replace("GaleSong", "GaleSong.UI");
                return name.EndsWith("Dialog") ? name.Replace("Dialog", "Form") : name + "Form";
            };

        private static readonly Dictionary<string, ViewItem> viewCaches = new Dictionary<string, ViewItem>();

        public static object CreateView(IView view)
        {
            if(!view.Cacheable)
            {
                return CreateViewCore(view);
            }

            ViewItem viewItem;
            viewCaches.TryGetValue(view.Name, out viewItem);
            if(viewItem == null)
            {
                object control = CreateViewCore(view);
                viewItem = new ViewItem { View = control, Strategy = view.ViewStrategy };
                viewCaches.Add(view.Name, viewItem);
            }
            else if(viewItem.Strategy != view.ViewStrategy)
            {
                DeleteViewCore(viewItem, view);
                object control = CreateViewCore(view);
                viewCaches[view.Name].Strategy = view.ViewStrategy;
                viewCaches[view.Name].View = control;
            }
            return viewCaches[view.Name].View;
        }

        private static object CreateViewCore(IView view)
        {
            Type type;
            if(view.ViewStrategy == ViewStrategy.Wpf)
            {
                type =
                    AssemblySource.Instance.Select(
                        item =>
                            Type.GetType(string.Format("{0}, {1}", ResolveWpfName(view.GetType().FullName),
                                item.FullName))).FirstOrDefault(it => it != null);
            }
            else
            {
                type =
                    AssemblySource.Instance.Select(
                        item =>
                            Type.GetType(string.Format("{0}, {1}", ResolveWinformName(view.GetType().FullName),
                                item.FullName))).FirstOrDefault(it => it != null);
            }

            if(type == null)
            {
                throw new FileNotFoundException(string.Format("[ViewHelper] Can not find View Type for {0}",
                    view.GetType().Name));
            }

            object control = EmitEngine.CreateInstance(type);

            var element = control as FrameworkElement;
            if(element != null)
            {
                element.DataContext = view;
            }

            var bindable = control as IBindable;
            if(bindable != null)
            {
                bindable.SetBinding(view);
            }

            return control;
        }

        private static void DeleteViewCore(ViewItem item, IView page)
        {
            if(item.Strategy == ViewStrategy.Winform)
            {
                var bindable = item.View as IBindable;
                if(bindable != null)
                {
                    bindable.ClearBinding(page);
                }
            }
        }

        private class ViewItem
        {
            public ViewStrategy Strategy;
            public object View;
        }
    }

    public interface IBindable
    {
        void SetBinding(object dataContext);
        void ClearBinding(object dataContext);
    }
}